package io.gitee.caoxiaoyu97.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.read.metadata.ReadWorkbook;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.WriteWorkbook;
import io.gitee.caoxiaoyu97.ConfigUtils;
import io.gitee.caoxiaoyu97.excel.handler.CenterTitleHandler;
import io.gitee.caoxiaoyu97.excel.handler.ColumnWidthHandler;
import io.gitee.caoxiaoyu97.excel.handler.LeftExportDescribeHandler;
import io.gitee.caoxiaoyu97.excel.handler.WaterMarkHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class EasyExcelUtil {

    private static final String KEY_USE_WATER_MARK = "use.water.mark";

    private static final boolean DEFAUL_USE_WATER_MARK = false;
    private static final String KEY_WATER_MARK_VALUE = "water.mark";
    private static final String DEFAULT_WATER_MARK_VALUE = "奥里给给";


    private static final String DEFAULT_SHEET_NAME = "数据内容";


    public static List<List<String>> readExcelWithStringList(InputStream inputStream, ExcelTypeEnum excelTypeEnum) {
        StringExcelListener listener = new StringExcelListener();
        ReadWorkbook readWorkbook = new ReadWorkbook();
        readWorkbook.setInputStream(inputStream);
        readWorkbook.setExcelType(excelTypeEnum);
        readWorkbook.getCustomReadListenerList().add(listener);
        ExcelReader excelReader = new ExcelReader(readWorkbook);
        excelReader.readAll();
        return listener.getDatas();
    }

    public static void writeExcelWithStringList(OutputStream outputStream, List<List<String>> data, WriteTable table, ExcelTypeEnum excelTypeEnum) {
        WriteWorkbook writeWorkbook = new WriteWorkbook();
        writeWorkbook.setOutputStream(outputStream);
        writeWorkbook.setExcelType(excelTypeEnum);
        writeWorkbook.setNeedHead(false);
        ExcelWriter writer = new ExcelWriter(writeWorkbook);
        writer.write(data,new WriteSheet(),table);
        writer.finish();
    }

    public static <E> List<E> readExcelWithModel(InputStream inputStream, Class<?> clazz, ExcelTypeEnum excelTypeEnum) {
        return readExcelWithModel(inputStream, clazz, excelTypeEnum, 1, 1);
    }

    public static List readExcelWithModel(InputStream inputStream, Class<?> clazz, ExcelTypeEnum excelTypeEnum, int sheetNo, int headLineNum) {
        ModelExcelListener listener = new ModelExcelListener();
        ((ExcelReaderSheetBuilder)EasyExcel.read(inputStream, clazz, listener).excelType(excelTypeEnum).sheet(sheetNo - 1).headRowNumber(headLineNum)).doRead();
        return listener.getDataList();
    }

    public static <E> List<E> readExcelWithModel(String fullFileName, Class<?> clazz, ExcelTypeEnum excelTypeEnum) {
        try (InputStream inputStream = new FileInputStream(fullFileName);){
            return readExcelWithModel(inputStream, clazz, excelTypeEnum);
        } catch (Exception e) {
            throw new RuntimeException("读取" + fullFileName + " 文件失败",e);
        }
    }

    public static void writeExcelWithModel(OutputStream outputStream, List<?> data, ExcelTypeEnum excelType, String centerTitle, List<String> leftDescribeList) {
        Class<?> clazz = data.get(0).getClass();
        log.info("写入excel数据开始：数据量=[{}]，类型=[{}] ---->>>>>>>", data.size(), clazz.getName());
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream, clazz).excelType(excelType);
        if (ConfigUtils.get(KEY_USE_WATER_MARK, Boolean.class, DEFAUL_USE_WATER_MARK)) {
            excelWriterBuilder.inMemory(true).registerWriteHandler(new WaterMarkHandler(getWaterMark()));
        } else {
            excelWriterBuilder.registerWriteHandler(new ColumnWidthHandler());
        }

        ExcelWriterSheetBuilder builder = excelWriterBuilder.sheet(DEFAULT_SHEET_NAME);
        int headRowNum = 0;
        Integer lastColumnNum = null;
        if (StringUtils.isNotEmpty(centerTitle)) {
            lastColumnNum = getLastColumnNum(clazz);
            builder.registerWriteHandler(new CenterTitleHandler(DEFAULT_SHEET_NAME, headRowNum, centerTitle, lastColumnNum));
            ++headRowNum;
        }

        if (CollectionUtils.isNotEmpty(leftDescribeList)) {
            for (String leftDesc : leftDescribeList) {
                if (!StringUtils.isEmpty(leftDesc)) {
                    if (lastColumnNum == null) {
                        lastColumnNum = getLastColumnNum(clazz);
                    }
                    builder.registerWriteHandler(new LeftExportDescribeHandler(DEFAULT_SHEET_NAME, headRowNum, leftDesc, lastColumnNum));
                    ++headRowNum;
                }
            }
        }

        builder.relativeHeadRowIndex(headRowNum).doWrite(data);
        log.info("写入excel数据结束：数据量=[{}]，类型=[{}] <<<<<<<----\n", data.size(), clazz.getName());
    }

    public static void writeExcelWithModel(OutputStream outputStream, List<?> data, ExcelTypeEnum excelType) {
        writeExcelWithModel(outputStream, data,excelType, null,null);
    }



    private static int getLastColumnNum(Class<?> clazz) {
        int columnNum = getColumnCount(clazz) - 1;
        return Math.max(columnNum, 0);
    }

    private static int getColumnCount(Class<?> clazz) {
        int columnCount = 0;
        Field[] var2 = clazz.getDeclaredFields();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Field field = var2[var4];
            ExcelProperty excelProperty = (ExcelProperty)field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                ++columnCount;
            }
        }

        return columnCount;
    }

    private static String getWaterMark() {
        String waterMark = ConfigUtils.get(KEY_WATER_MARK_VALUE, DEFAULT_WATER_MARK_VALUE);;
        return waterMark;
    }
    public static <T> T convertExcelBO(String fileName, String[] contents, T bo) {
        List<Field> fieldList = FieldUtils.getAllFieldsList(bo.getClass());
        long len = fieldList.stream().filter((f) -> f.getAnnotation(ExcelProperty.class) != null).count();
        if (contents.length != len) {
            log.error("文件内容长度[{}]字段长度[{}]不一致", contents.length, len);
            if (StringUtils.isEmpty(fileName)) {
                throw new RuntimeException("文件内容长度不一致");
            } else {
                throw new RuntimeException(fileName + "文件内容与字段长度不一致");
            }
        } else {
            fieldList.stream().filter((f) -> f.getAnnotation(ExcelProperty.class) != null).forEach((field) -> {
                field.setAccessible(true);
                try {
                    int index = (field.getAnnotation(ExcelProperty.class)).index();
                    Object val = contents[index].trim();
                    if (field.getType().isAssignableFrom(BigDecimal.class)) {
                        val = new BigDecimal(contents[index].trim());
                    }

                    field.set(bo, val);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("数据转对象失败",e);
                }
            });
        }
        return bo;
    }

    private static class ModelExcelListener<E> extends AnalysisEventListener<E> {
        private List<E> dataList;

        private ModelExcelListener() {
            this.dataList = new ArrayList();
        }

        public void invoke(E object, AnalysisContext context) {
            this.dataList.add(object);
        }

        public void doAfterAllAnalysed(AnalysisContext context) {
        }

        public List<E> getDataList() {
            return this.dataList;
        }

        public void setDataList(List<E> dataList) {
            this.dataList = dataList;
        }
    }

    private static class StringExcelListener extends AnalysisEventListener {
        private List<List<String>> datas;

        private StringExcelListener() {
            this.datas = new ArrayList();
        }

        public void invoke(Object object, AnalysisContext context) {
            List<String> stringList = (List)object;
            this.datas.add(stringList);
        }

        public void doAfterAllAnalysed(AnalysisContext context) {
        }

        public List<List<String>> getDatas() {
            return this.datas;
        }

        public void setDatas(List<List<String>> datas) {
            this.datas = datas;
        }
    }
}
